use std::{
    ffi::{c_int, c_void},
    ptr::null_mut,
    sync::{
        Arc, Condvar, Mutex
    },
    thread::{sleep, Builder}, time::{Duration, Instant},
};

use crossbeam_channel::{bounded, select, Receiver, Sender};
use ffmpeg::{
    av_fifo_read_safe, av_frame_alloc_safe, av_frame_free_safe, av_packet_alloc_safe, av_packet_free_safe, av_packet_move_ref_safe, av_packet_unref_safe, av_q2d_safe, avcodec_alloc_context3_safe, avcodec_close_safe, avcodec_find_decoder_safe, avcodec_flush_buffers_safe, avcodec_open2_safe, avcodec_parameters_to_context_safe, avcodec_receive_frame_safe, avcodec_send_packet_safe, sws_free_context_safe, sws_get_context_safe, sws_scale_safe
};
use ffmpeg_sys_next::{
    AVCodec, AVCodecContext, AVCodecParameters, AVERROR, AVERROR_EOF, AVFrame, AVPixelFormat,
    AVRational, EAGAIN, SWS_BILINEAR,
};
use sdl2::{
    event::{Event, WindowEvent}, keyboard::Keycode, pixels::PixelFormatEnum, rect::Rect, Sdl, VideoSubsystem
};

use crate::{Broadcaster, CustomError, MediaStatus, Packet, PacketFifo, Timer, VideoDecoderData, MAX_FRAME_SIZE, MIN_QUEUE_SIZE, CodecContextSafe, VideoDevice};

pub struct VideoPlayer {
    pub width: Arc<Mutex<u32>>,
    pub height: Arc<Mutex<u32>>,
    pub original_width: u32,
    pub original_height: u32,
    pub window_width: u32,
    pub window_height: u32,
    pub time_base: AVRational,
    pub index: isize,
    pub pix_fmt: AVPixelFormat,
    pub video_codec_parameters: *mut AVCodecParameters,
    pub video_codec: *mut AVCodec,
    pub av_codec_context: *mut AVCodecContext,
    pub title: String,
    pub sdl: Sdl,
    pub video_subsystem: VideoSubsystem,
    pub packet_fifo: Arc<Mutex<PacketFifo>>,
    pub condvar: Arc<Condvar>,
    pub recevier: Option<Arc<Mutex<Receiver<VideoDecoderData>>>>,
    pub timer: Arc<Mutex<Timer>>,
    pub media_status: Arc<MediaStatus>,
    pub seek_sender: Broadcaster<i8>,
    pub seek_receiver: Receiver<i8>,
}

impl VideoPlayer {
    pub fn new(title: String, sdl: Sdl, packet_fifo: Arc<Mutex<PacketFifo>>, condvar: Arc<Condvar>, timer: Arc<Mutex<Timer>>, media_status: Arc<MediaStatus>, seek_sender: Broadcaster<i8>, seek_receiver: Receiver<i8>) -> Self {
        let video_subsystem = sdl.video().unwrap();
        VideoPlayer {
            width: Arc::new(Mutex::new(0)),
            height: Arc::new(Mutex::new(0)),
            original_width: 0,
            original_height: 0,
            window_width: 0,
            window_height: 0,
            time_base: AVRational { num: 0, den: 0 },
            pix_fmt: AVPixelFormat::AV_PIX_FMT_NONE,
            index: -1,
            video_codec_parameters: null_mut(),
            video_codec: null_mut(),
            av_codec_context: null_mut(),
            title,
            sdl,
            video_subsystem,
            packet_fifo,
            condvar,
            recevier: None,
            timer,
            media_status,
            seek_sender,
            seek_receiver,
        }
    }

    pub fn avcodec_alloc_context(mut self) -> Result<Self, CustomError> {
        let av_codec_context = avcodec_alloc_context3_safe(null_mut());
        if av_codec_context.is_null() {
            return Err(CustomError::FfmepgError("Unsupported codec".to_owned()));
        }
        self.av_codec_context = av_codec_context;

        Ok(self)
    }

    pub fn avcodec_find_decoder(mut self) -> Result<Self, CustomError> {
        //根据音视频流对应的解码器上下文查找对应的解码器，返回对应的解码器(信息结构体)
        let av_codec = avcodec_find_decoder_safe(unsafe { (*self.av_codec_context).codec_id });
        if av_codec.is_null() {
            return Err(CustomError::FfmepgError("Unsupported codec".to_owned()));
        }
        self.video_codec = av_codec as *mut AVCodec;
        Ok(self)
    }

    pub fn avcodec_parameters_to_context(self) -> Result<Self, CustomError> {
        if avcodec_parameters_to_context_safe(self.av_codec_context, self.video_codec_parameters)
            != 0
        {
            return Err(CustomError::FfmepgError(
                "Couldn't copy codec context".to_owned(),
            ));
        }
        Ok(self)
    }

    pub fn avcodec_open(mut self) -> Result<Self, CustomError> {
        if avcodec_open2_safe(self.av_codec_context, self.video_codec, null_mut()) != 0 {
            return Err(CustomError::FfmepgError("Could not open codec.".to_owned()));
        }
        self.pix_fmt = unsafe { (*self.av_codec_context).pix_fmt };
        Ok(self)
    }

    pub fn init_window(mut self) -> Result<Self, CustomError> {
        if let Ok(rect) = self.video_subsystem.display_bounds(0) {
            let screen_width = rect.width();
            let screen_height = rect.height();
            let (mut width, mut height) = (self.original_width, self.original_height);
            let (video_width, video_height) = (self.original_width, self.original_height);
            if video_width > screen_width || video_height > screen_height {
                self.window_width = screen_width;
                self.window_height = screen_height;
                
                (width, height) = Self::reset_window(
                    self.width.clone(),
                    self.height.clone(),
                    screen_width, 
                    screen_height, 
                    self.original_width, 
                    self.original_height, 
                )?;
            }
            
            self.width = Arc::new(Mutex::new(width));
            self.height = Arc::new(Mutex::new(height));
        };
        self.window_width = self.original_width;
        self.window_height = self.original_height;
        Ok(self)
    }

    pub fn reset_window(
        width: Arc<Mutex<u32>>,
        height: Arc<Mutex<u32>>,
        window_width: u32,
        window_height: u32,
        original_width: u32,
        original_height: u32,
    ) -> Result<(u32, u32), CustomError> {
        let mut width_lock: std::sync::MutexGuard<'_, u32> = width.lock().unwrap();
        let mut height_lock = height.lock().unwrap();

        let (width, height);
        let width_ratio = original_width as f64 / window_width as f64;
        let height_ratio = original_height as f64 / window_height as f64;
        if width_ratio >= height_ratio {
            (width, height) = (window_width, (window_width * original_height / original_width));
        } else {
            (width, height) = ((window_height * original_width / original_height), window_height);
        }
        *width_lock = width;
        *height_lock = height;

        Ok((width, height))
    }

    pub fn decode_packet(mut self) -> Result<Self, CustomError> {
        let packet_fifo = self.packet_fifo.clone();
        let condvar = self.condvar.clone();
        let codec_context = CodecContextSafe {
            av_codec_context: self.av_codec_context,
        };
        let width = self.width.clone();
        let height = self.height.clone();
        let pix_fmt = self.pix_fmt;
        let (sender, recevier): (Sender<VideoDecoderData>, Receiver<VideoDecoderData>) = bounded(MAX_FRAME_SIZE);
        self.recevier = Some(Arc::new(Mutex::new(recevier)));
        let time_base = self.time_base;
        let media_status = self.media_status.clone();

        Builder::new()
            .name("video decode thread".to_string())
            .spawn(move || {
                let _ = Self::loop_decode_packet(
                    codec_context,
                    packet_fifo,
                    condvar,
                    width,
                    height,
                    pix_fmt,
                    time_base,
                    sender,
                    media_status,
                );
            })
            .unwrap();
        Ok(self)
    }

    pub fn loop_decode_packet(
        codec_context: CodecContextSafe,
        packet_fifo: Arc<Mutex<PacketFifo>>,
        condvar: Arc<Condvar>,
        width: Arc<Mutex<u32>>,
        height: Arc<Mutex<u32>>,
        pix_fmt: AVPixelFormat,
        time_base: AVRational,
        sender: Sender<VideoDecoderData>,
        media_status: Arc<MediaStatus>,
    ) -> Result<(), CustomError> {
        loop {
            media_status.check_pause()?;

            let packet = {
                let mut packet_fifo = packet_fifo.lock().unwrap();
                let mut src_packet = Packet::new();
                let res = av_fifo_read_safe((*packet_fifo).fifo, &mut src_packet as *mut Packet as *mut c_void, 1);
                if res < 0 {
                    let mut status_lock = media_status.status.lock().unwrap();
                    if (*status_lock).packet_end() {
                        (*status_lock).set_video_frame_end();
                        return Ok(());
                    } else if (*status_lock).audio_frame_end() {
                        (*status_lock).set_play_end();
                        return Ok(());
                    } else {
                        continue;
                    }
                }

                if packet_fifo.size() == 0 {
                    continue;
                }
                let curr_size = packet_fifo.size() - 1;
                packet_fifo.put_size(curr_size);
                if curr_size < MIN_QUEUE_SIZE {
                    condvar.notify_all();
                }

                let dst_packet = av_packet_alloc_safe();
                av_packet_move_ref_safe(dst_packet, src_packet.pkt);
                av_packet_free_safe(&mut src_packet.pkt);
                dst_packet
            };

            if avcodec_send_packet_safe(codec_context.av_codec_context, packet) < 0 {
                av_packet_unref_safe(packet);
                println!("Failed to decode packet.");
                return Err(CustomError::FfmepgError(
                    "Failed to decode packet".to_owned(),
                ));
            }
            av_packet_unref_safe(packet);

            loop {
                let (dst_width, dst_height) = {
                    (*width.lock().unwrap(), *height.lock().unwrap())
                };

                let mut decoder_data: Vec<u8> = Vec::new();
                let video_time = {
                    let mut frame = av_frame_alloc_safe();
                    let ret = avcodec_receive_frame_safe(codec_context.av_codec_context, frame);
                    if ret == AVERROR(EAGAIN) || ret == AVERROR_EOF {
                        av_frame_free_safe(&mut frame);
                        break;
                    } else if ret < 0 {
                        av_frame_free_safe(&mut frame);
                        return Err(CustomError::FfmepgError("Decoding error".to_owned()));
                    }

                    let width = unsafe { (*frame).width };
                    let height = unsafe { (*frame).height };

                    decoder_data.reserve_exact((dst_width * dst_height * 4) as usize);
                    let dest: [*mut u8; 1] = [decoder_data.as_mut_ptr(); 1];
                    let dest_linesize = [dst_width as i32 * 3];
                    
                    let _ = Self::sws_scale(
                        frame,
                        width,
                        height,
                        dest_linesize,
                        dest,
                        dst_width as i32,
                        dst_height as i32,
                        pix_fmt,
                    );
                    
                    unsafe {
                        (*frame).pts = (*frame).best_effort_timestamp;
                    }
                    
                    let video_time = (unsafe { (*frame).pts }) as f64 * av_q2d_safe(time_base) * 1000_f64;
                    av_frame_free_safe(&mut frame);
                    video_time
                };
                {
                    let _ = sender.send(VideoDecoderData::new(decoder_data, dst_width, dst_height, video_time));
                }
            }
        }
        //Ok(())
    }

    pub fn sws_scale(
        frame: *mut AVFrame,
        src_width: i32,
        src_height: i32,
        dest_linesize: [i32; 1],
        dest: [*mut u8; 1],
        dst_width: i32,
        dst_height: i32,
        pix_fmt: AVPixelFormat,
    ) -> Result<(), CustomError> {
        let sws_context = sws_get_context_safe(
            src_width,
            src_height,
            pix_fmt,
            dst_width,
            dst_height,
            AVPixelFormat::AV_PIX_FMT_RGB24,
            SWS_BILINEAR,
            null_mut(),
            null_mut(),
            null_mut(),
        );

        let res = sws_scale_safe(
            sws_context,
            unsafe { (*frame).data.as_ptr() } as *const *const u8,
            unsafe { (*frame).linesize.as_ptr() } as *const c_int,
            0,
            src_height,
            dest.as_ptr(),
            dest_linesize.as_ptr(),
        );
        sws_free_context_safe(sws_context);
        if res < 0 {
            return Err(CustomError::FfmepgError(
                "视频像素格式和分辨率的转换失败.".to_owned(),
            ));
        }
        Ok(())
    }

    pub fn player(self) -> Result<Self, CustomError> {
        let title = self.title.clone();
        let window_width = self.window_width;
        let window_height = self.window_height;
        let original_width = self.original_width;
        let original_height = self.original_height;
        let width = self.width.clone();
        let height = self.height.clone();
        let receiver = self.recevier.clone().unwrap();
        let timer = self.timer.clone();
        let media_status = self.media_status.clone();
        let seek_sender = self.seek_sender.clone();
        let seek_receiver = self.seek_receiver.clone();
        let packet_fifo = self.packet_fifo.clone();
        let codec_context = CodecContextSafe {
            av_codec_context: self.av_codec_context,
        };
        let video_device = VideoDevice {
            sdl: self.sdl.clone(),
            video_subsystem: self.video_subsystem.clone(),
        };
        let condvar = self.condvar.clone();

        Builder::new()
            .name("video player thread".to_string())
            .spawn(move || {
                let _ = Self::loop_player(
                    title, 
                    width,
                    height,
                    window_width, 
                    window_height, 
                    original_width,
                    original_height,
                    receiver, 
                    timer, 
                    media_status, 
                    video_device, 
                    seek_sender, 
                    seek_receiver, 
                    codec_context, 
                    packet_fifo,
                    condvar,
                );
            })
            .unwrap();
        
        Ok(self)
    }

    pub fn loop_player(
        title: String,
        width: Arc<Mutex<u32>>,
        height: Arc<Mutex<u32>>,
        mut window_width: u32,
        mut window_height: u32,
        original_width: u32,
        original_height: u32,
        receiver: Arc<Mutex<Receiver<VideoDecoderData>>>,
        timer: Arc<Mutex<Timer>>,
        media_status: Arc<MediaStatus>,
        video_device: VideoDevice,
        seek_sender: Broadcaster<i8>,
        seek_receiver: Receiver<i8>,
        codec_context: CodecContextSafe,
        packet_fifo: Arc<Mutex<PacketFifo>>,
        condvar: Arc<Condvar>,
    ) -> Result<(), CustomError> {
        let window = video_device
            .video_subsystem
            .window(&title, window_width as u32, window_height)
            .resizable()
            .position_centered()
            .opengl()
            .build()
            .map_err(|e| e.to_string())
            .unwrap();

        let mut canvas = window
            .into_canvas()
            .build()
            .map_err(|e| e.to_string())
            .unwrap();
        let texture_creator = canvas.texture_creator();
        let mut texture;

        let mut event_pump = video_device.sdl.event_pump().unwrap();

        //let mut curr_status: PlayStatus = PlayStatus::new();
        let mut stop_time = Instant::now();

        'running: loop {
            select! {
                recv(seek_receiver) -> _seek => {
                    std::thread::sleep(std::time::Duration::from_millis(100));
                    avcodec_flush_buffers_safe(codec_context.av_codec_context);
                   {

                       let mut packet_fifo = packet_fifo.lock().unwrap();
                       loop {
                           let mut src_packet = Packet::new();
                           let res = av_fifo_read_safe((*packet_fifo).fifo, &mut src_packet as *mut Packet as *mut c_void, 1);
                           if res < 0 {
                               packet_fifo.put_size(0);
                               println!("video fifo break;");
                               break;
                            }
                            av_packet_free_safe(&mut src_packet.pkt);
                        }
                    } 
                    
                    let receiver_lock = receiver.lock().unwrap();
                    loop {
                        match receiver_lock.try_recv() {
                            Ok(_) => {},
                            _ => {
                                println!("video receiver break;");
                                //condvar.notify_all();
                                break
                            },
                        }
                    }
                },
                default => {

                },
            }
            {
                let status_lock = media_status.status.lock().unwrap();
                if (*status_lock).play_end() {
                    break;
                }
            }

            for event in event_pump.poll_iter() {
                match event {
                    Event::Quit { .. }
                    | Event::KeyDown {
                        keycode: Some(Keycode::Escape),
                        ..
                    } => {
                        break 'running;
                    }
                    Event::KeyDown {
                        keycode: Some(Keycode::Space),
                        ..
                    } => {
                        match media_status.pause_or_resume()? {
                            Some(time) => {
                                stop_time = time;
                            },
                            None => {
                                timer.lock().unwrap().subtract_time(stop_time.elapsed());
                            },
                        };
                    }
                    Event::KeyDown {
                        timestamp: _,
                        window_id: _,
                        keycode,
                        scancode: _,
                        keymod: _,
                        repeat: _,
                    } => {
                        {
                            let mut status_lock = media_status.status.lock().unwrap();
                            (*status_lock).set_pause();
                            match keycode {
                                Some(Keycode::Right) => {
                                    println!("快进");
                                    let _ = seek_sender.broadcast(10);
                                }
                                Some(Keycode::Left) => {
                                    println!("快退");
                                    let _ = seek_sender.broadcast(-10);
                                }
                                _ => {}
                            };
                        }
                    }
                    Event::Window {
                        timestamp: _,
                        window_id: _,
                        win_event,
                    } => {
                        if let WindowEvent::Resized(w_width, w_height) = win_event {
                            window_width = w_width as u32;
                            window_height = w_height as u32;
                            Self::reset_window(
                                width.clone(),
                                height.clone(),
                                window_width as u32, 
                                window_height as u32, 
                                original_width, 
                                original_height,
                            )?;
                        }
                    }
                    _ => {}
                }
            }
            
            let (data, width, height, video_time) = {
                let receiver_lock = receiver.lock().unwrap();
                match receiver_lock.try_recv() {
                    Ok(decoder_data) => (decoder_data.data, decoder_data.width, decoder_data.height, decoder_data.time),
                    _ => continue,
                }
            };

            texture = texture_creator
                .create_texture_target(PixelFormatEnum::RGB24, width as u32, height as u32)
                .unwrap();
            texture
                .update(None, data.as_slice(), (width * 3) as usize)
                .expect("update texture error");
            canvas.clear();
            canvas
                .copy(
                    &texture,
                    None,
                    Some(Rect::new((window_width as i32 - width as i32) / 2, (window_height as i32 - height as i32) / 2, width as u32, height as u32,)),
                )
                .unwrap();

            canvas.present();
            
            let run_time = {
                timer.lock().unwrap().total_elapsed().as_millis() as i64
            };
            let sleep_time = video_time as i64 - run_time;
            //println!("video run_time: {}, video time: {}, sleep time: {}", run_time, video_time, sleep_time);
            
            if sleep_time > 0 {
                sleep(Duration::from_millis(sleep_time as u64));
            }
        }

        Ok(())
    }
    
    pub fn close(self) -> Result<Self, CustomError> {
        avcodec_close_safe(self.av_codec_context);
        Ok(self)
    }
}
